%{
package parser

import (
	"fmt"

)

%}

%union {
	dummy int
}


%token	  tLITERAL
%token	  tASOP
%token	  tBREAK tCASE tCHAN tCOLAS tCONST tCONTINUE tDDD
%token	  tDEFAULT tDEFER tELSE tFALL tFOR tFUNC tGO tGOTO
%token	  tIF tIMPORT tINTERFACE tMAP
%token	  tNAME
%token	  tPACKAGE tRANGE tRETURN tSELECT tSTRUCT tSWITCH
%token	  tTYPE tVAR
%token    tXLEFT tXRIGHT

%token	  tAND_AND tAND_NOT tBODY tCOMM tDEC tEQ tGE tGT
%token	  tIGNORE tINC tLE tLSH tLT tNE tOR_OR tRSH

%left		tCOMM	/* outside the usual hierarchy; here for good error messages */

%left		tOR_OR
%left		tAND_AND
%left		tEQ tNE tLE tGE tLT tGT
%left		'+' '-' '|' '^'
%left		'*' '/' '%' '&' tLSH tRSH tAND_NOT

/*
 * manual override of shift/reduce conflicts.
 * the general form is that we assign a precedence
 * to the token being shifted and then introduce
 * NotToken with lower precedence or PreferToToken with higher
 * and annotate the reducing rule accordingly.
 */
%left		notPackage
%left		tPACKAGE

%left		notParen
%left		'('

%left		')'
%left		preferToRightParen

%%

file:
	package
	imports
	xdcl_list

package:
	%prec notPackage
|	tPACKAGE sym ';'

imports:
|	imports import ';'

import:
	tIMPORT import_stmt
|	tIMPORT '(' import_stmt_list osemi ')'
|	tIMPORT '(' ')'

import_stmt:
	import_here

import_stmt_list:
	import_stmt
|	import_stmt_list ';' import_stmt

import_here:
	tLITERAL
|	sym tLITERAL
|	'.' tLITERAL

/*
 * declarations
 */
xdcl:
|	common_dcl
|	xfndcl
|	non_dcl_stmt
|	error

common_dcl:
	tVAR vardcl
|	tVAR '(' vardcl_list osemi ')'
|	tVAR '(' ')'
|	lconst constdcl
|	lconst '(' constdcl osemi ')'
|	lconst '(' constdcl ';' constdcl_list osemi ')'
|	lconst '(' ')'
|	tTYPE typedcl
|	tTYPE '(' typedcl_list osemi ')'
|	tTYPE '(' ')'

lconst:
	tCONST

vardcl:
	dcl_name_list ntype
|	dcl_name_list ntype '=' expr_list
|	dcl_name_list '=' expr_list

constdcl:
	dcl_name_list ntype '=' expr_list
|	dcl_name_list '=' expr_list

constdcl1:
	constdcl
|	dcl_name_list ntype
|	dcl_name_list

typedcl:
	sym ntype
|	sym xgo_params ntype

simple_stmt:
	expr
|	expr tASOP expr
|	expr_list '=' expr_list
|	expr_list tCOLAS expr_list
|	expr tINC
|	expr tDEC

case:
	tCASE expr_or_type_list ':'
|	tCASE expr_or_type_list '=' expr ':'
|	tCASE expr_or_type_list tCOLAS expr ':'
|	tDEFAULT ':'

compound_stmt:
	'{' stmt_list '}'

switch_body:
	tBODY caseblock_list '}'

caseblock_stmt_list:
	stmt
|	stmt_list ';' stmt

caseblock:
	case
	caseblock_stmt_list

caseblock_list:
|	caseblock_list caseblock

loop_body:
	tBODY stmt_list '}'

range_stmt:
	expr_list '=' tRANGE expr
|	expr_list tCOLAS tRANGE expr

for_header:
	osimple_stmt ';' osimple_stmt ';' osimple_stmt
|	osimple_stmt
|	range_stmt

for_body:
	for_header loop_body

for_stmt:
	tFOR
	for_body

if_header:
	osimple_stmt
|	osimple_stmt ';' osimple_stmt

if_stmt:
	tIF if_header loop_body

switch_stmt:
	tSWITCH if_header switch_body

select_stmt:
	tSELECT switch_body

/*
 * expressions
 */
expr:
	uexpr
|	expr tOR_OR expr
|	expr tAND_AND expr
|	expr tEQ expr
|	expr tNE expr
|	expr tLT expr
|	expr tLE expr
|	expr tGE expr
|	expr tGT expr
|	expr '+' expr
|	expr '-' expr
|	expr '|' expr
|	expr '^' expr
|	expr '*' expr
|	expr '/' expr
|	expr '%' expr
|	expr '&' expr
|	expr tAND_NOT expr
|	expr tLSH expr
|	expr tRSH expr
|	expr tCOMM expr

uexpr:
	pexpr
|	'*' uexpr
|	'&' uexpr
|	'+' uexpr
|	'-' uexpr
|	'!' uexpr
|	'~' uexpr
|	'^' uexpr
|	tCOMM uexpr

/*
 * call-like statements that
 * can be preceded by 'defer' and 'go'
 */
pseudocall:
	pexpr '(' ')'
|	pexpr '(' expr_or_type_list ocomma ')'
|	pexpr '(' expr_or_type_list tDDD ocomma ')'

pexpr_no_paren:
	tLITERAL
|	name
|	name xgo_args
|	pexpr '.' sym
|	pexpr '.' '(' expr_or_type ')'
|	pexpr '.' '(' tTYPE ')'
|	pexpr '[' expr ']'
|	pexpr '[' oexpr ':' oexpr ']'
|	pseudocall
|	convtype '(' expr ')'
|	comptype lbrace braced_keyval_list '}'
|	pexpr_no_paren '{' braced_keyval_list '}'
|	'(' expr_or_type ')' '{' braced_keyval_list '}'
|	fnliteral

keyval:
	expr ':' complitexpr

complitexpr:
	expr
|	'{' braced_keyval_list '}'

pexpr:
	pexpr_no_paren
|	'(' expr_or_type ')'

expr_or_type:
	expr
|	non_expr_type %prec preferToRightParen

name_or_type:
	ntype

lbrace:
	tBODY
|	'{'

/*
 * names and types
 *	newname is used before declared
 *	oldname is used after declared
 */
new_name:
	sym

dcl_name:
	sym

onew_name:
|	new_name

sym:
	tNAME

name:
	sym	%prec notParen

labelname:
	new_name

/*
 * to avoid parsing conflicts, type is split into
 *	channel types
 *	function types
 *	parenthesized types
 *	any other type
 * the type system makes additional restrictions,
 * but those are not implemented in the grammar.
 */
dotdotdot:
	tDDD
|	tDDD ntype

ntype:
	recvchantype
|	fntype
|	othertype
|	ptrtype
|	dotname
|	dotname xgo_args
|	'(' ntype ')'

non_expr_type:
	recvchantype
|	fntype
|	othertype
|	'*' non_expr_type

non_recvchantype:
	fntype
|	othertype
|	ptrtype
|	dotname
|	dotname xgo_args
|	'(' ntype ')'

convtype:
	fntype
|	othertype

comptype:
	othertype

fnret_type:
	recvchantype
|	fntype
|	othertype
|	ptrtype
|	dotname
|	dotname xgo_args

dotname:
	name
|	name '.' sym

othertype:
	'[' oexpr ']' ntype
|	'[' tDDD ']' ntype
|	tCHAN non_recvchantype
|	tCHAN tCOMM ntype
|	tMAP '[' ntype ']' ntype
|	structtype
|	interfacetype

ptrtype:
	'*' ntype

recvchantype:
	tCOMM tCHAN ntype

structtype:
	tSTRUCT lbrace structdcl_list osemi '}'
|	tSTRUCT lbrace '}'

interfacetype:
	tINTERFACE lbrace interfacedcl_list osemi '}'
|	tINTERFACE lbrace '}'

/*
 * xGo
 *
 * xgo rules make additional restrictions,
 * but those are not implemented in the grammar.
 */

xgo_params:
	tXLEFT xgo_paramlist ocomma tXRIGHT

xgo_paramlist:
	sym	    
|	xgo_paramlist ',' sym	   

xgo_args:
	tXLEFT xgo_arglist ocomma tXRIGHT

xgo_arglist:
	xgo_arg
|	xgo_arglist ',' xgo_arg

xgo_arg:
	recvchantype
|	fntype
|	othertype
|	dotname
|	dotname xgo_args
|	'(' xgo_arg ')'
|	'*' xgo_arg

/*
 * function stuff
 * all in one place to show how crappy it all is
 */
xfndcl:
	tFUNC fndcl fnbody
|	tFUNC xgo_params fndcl fnbody
|	tFUNC sym dotname xgo_args

fndcl:
	dcl_name '(' oarg_type_list_ocomma ')' fnres
|	'(' oarg_type_list_ocomma ')' sym '(' oarg_type_list_ocomma ')' fnres

fntype:
	tFUNC '(' oarg_type_list_ocomma ')' fnres

fnbody:
|	'{' stmt_list '}'

fnres:
	%prec notParen
|	fnret_type
|	'(' oarg_type_list_ocomma ')'

fnlitdcl:
	fntype

fnliteral:
	fnlitdcl lbrace stmt_list '}'
|       fnlitdcl error


/*
 * lists of things
 * note that they are left recursive
 * to conserve yacc stack. they need to
 * be reversed to interpret correctly
 */
xdcl_list:
|	xdcl_list xdcl ';'

vardcl_list:
	vardcl
|	vardcl_list ';' vardcl

constdcl_list:
	constdcl1
|	constdcl_list ';' constdcl1

typedcl_list:
	typedcl
|	typedcl_list ';' typedcl

structdcl_list:
	structdcl
|	structdcl_list ';' structdcl

interfacedcl_list:
	interfacedcl
|	interfacedcl_list ';' interfacedcl

structdcl:
	new_name_list ntype oliteral
|	embed oliteral
|	'(' embed ')' oliteral
|	'*' embed oliteral
|	'(' '*' embed ')' oliteral
|	'*' '(' embed ')' oliteral

packname:
	tNAME
|	tNAME '.' sym

embed:
	packname

interfacedcl:
	new_name indcl
|	packname
|	'(' packname ')'

indcl:
	'(' oarg_type_list_ocomma ')' fnres

/*
 * function arguments.
 */
arg_type:
	name_or_type
|	sym name_or_type
|	sym dotdotdot
|	dotdotdot

arg_type_list:
	arg_type
|	arg_type_list ',' arg_type

oarg_type_list_ocomma:
|	arg_type_list ocomma

/*
 * statement
 */
stmt:
|	compound_stmt
|	common_dcl
|	non_dcl_stmt
|	error

non_dcl_stmt:
	simple_stmt
|	for_stmt
|	switch_stmt
|	select_stmt
|	if_stmt
|	if_stmt tELSE stmt
|	labelname ':' stmt
|	tFALL
|	tBREAK onew_name
|	tCONTINUE onew_name
|	tGO pseudocall
|	tDEFER pseudocall
|	tGOTO new_name
|	tRETURN oexpr_list

stmt_list:
	stmt
|	stmt_list ';' stmt

new_name_list:
	new_name
|	new_name_list ',' new_name

dcl_name_list:
	dcl_name
|	dcl_name_list ',' dcl_name

expr_list:
	expr
|	expr_list ',' expr

expr_or_type_list:
	expr_or_type
|	expr_or_type_list ',' expr_or_type

/*
 * list of combo of keyval and val
 */
keyval_list:
	keyval
|	complitexpr
|	keyval_list ',' keyval
|	keyval_list ',' complitexpr

braced_keyval_list:
|	keyval_list ocomma

/*
 * optional things
 */
osemi:
|	';'

ocomma:
|	','

oexpr:
|	expr

oexpr_list:
|	expr_list

osimple_stmt:
|	simple_stmt

oliteral:
|	tLITERAL
